// 计算器服务器

#include "TcpServer.hpp"
#include <memory>
#include "Protocol.hpp"
#include <signal.h>
#include "daemon.hpp"
using namespace ns_tcpserver;
using namespace ns_protocol;

static void Usage(const std::string &process)
{
    std::cout << "\nUsage: " << process << " port\n"
              << std::endl;
}

// void debug(int sock)
// {
//     std::cout<<"我是测试服务，得到的sock："<<sock<<std::endl;
// }

static Response calculatorHelp(const Request &req)
{
    Response resp(0, 0,req.x_,req.y_,req.op_);
    switch (req.op_)
    {
    case '+':
        resp.result_ = req.x_ + req.y_;
        break;
    case '-':
        resp.result_ = req.x_ - req.y_;
        break;
    case '*':
        resp.result_ = req.x_ * req.y_;
        break;
    case '/':
        if (0 == req.y_)
            resp.code_ = 1;
        else
            resp.result_ = req.x_ / req.y_;
        break;
    case '%':
        if (0 == req.y_)
            resp.code_ = 2;
        else
            resp.result_ = req.x_ % req.y_;
        break;
    default:
        resp.code_ = 3;
    }
    return resp;
}
void calculator(int sock) // 传sock进来的原因是，要从sock中拿数据，处理完后要放回sock中，这个sock其实是serversock，服务员套接字
{
    std::string inbuffer;
    while (true)
    {
        // 1.读取数据-从接收缓冲区中读上来数据
        bool res = Recv(sock, &inbuffer /*输出型参数*/); // 服务端从serversock中收到数据 ：在这里我们读到了一个请求
        if (!res)
            break; // 若读取失败则不继续执行服务，直接break;退出服务端
            
        // std::cout<<"begin: 开始时-inbuffer: "<<inbuffer<<std::endl;//读取成功，输出inbuffer中临时的报文数据

        // 2.读取成功了，进行协议解析，保证得到一个完整的报文--解析
        std::string package = Decode(inbuffer); // 从带lenth和特殊分隔符的字符串中解析出目的的字符串(报文)
        if (package.empty())//解析失败，则继续重新读取数据进行解析
            continue; // 报文不完整，或者没有有效数据。则继续去获取报文
            //这里已经保证是一个完整的报文了

        // std::cout<<"end: 处理完完整报文之后，删减后，剩余-inbuffer: "<<inbuffer<<std::endl;
        // std::cout<<"解析出来的报文正文-package: "<<package<<std::endl;


        logMessage(NORMAL,"question: %s",package.c_str()/*解析之后的package报文正文*/);
        //3.解析成功，进行反序列化
        Request req;
        req.Deserialize(package);                  // 反序列化，把字符串结构转化成结构化数据

        //4.进行计算(反序列化，计算，序列化)--业务逻辑
        Response resp = calculatorHelp(req);       // 计算得计算结果

        //5.对计算结果，进行序列化
        std::string respString = resp.Serialize(); // 计算机结构结构化数据序列化转化成字符串

        logMessage(NORMAL,"ans: %s",respString.c_str()/*添加报文信息之后的答案*/);
        // std::cout<<"序列化后的计算结果-respString: "<<respString<<std::endl;

        //6.对要发送的数据进行加协议，添加长度信息，形成报文
        respString =Encode(respString);

        // std::cout<<"添加协议之后的报文-respString: "<<respString<<std::endl;

        //这里还要添加条件，因为在网络拥堵，数据没有完整写入发送缓冲区，不能发送。后面讲多路转接的时候再添加这个条件

        //7.发送-先拷贝到自己的发送缓冲区，再通过serversock发送到对方的接收缓冲区，让对方读取。
        Send(sock, respString);                    // 服务端发送结果到serversock中，此时与serversock链接的客户端若关闭了，则会报错。
    }
}

void handler(int signo)
{
    std::cout << "get a signo: " << signo << std::endl;
}
// 服务器1=
//  ./CalServer port
int main(int argc, char *argv[]) // 指定服务端端口号
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }

    // 由于客户端关闭而可能会报错的情况
    // 1.cleint与serversock关闭了链接,我才写
    // 2.我正在写，client与serversock关闭了链接
    // 解决方法1.
    //  signal(SIGPIPE,handler);//来忽略报错的信号，使服务端的进程不退出
    // 解决方法2.
    // 对rcv做判断，服务端读取成功了再写给serversock。否则不处理
    // 关于客户端退出导致服务端退出的异常情况的总结：
    // 一般经验:server在编写的时候，要有交委严谨性的逻辑判断
    // 一般服务器,都是要忽略SIGPIPE信号的，防止在运行中出现非法写入导致进程退出的问题

    // std::unique_ptr<TcpServer> server(new TcpServer(atoi(argv[1])));
    // server->BindService(calculator); //(绑定服务)网络功能
    // server->Start();                 //(处理数据)服务功能

    // //test
    // Request req(123,456,'+');
    // std::string s= req.Serialize();
    // std::cout<<s<<std::endl;

    // Request temp;
    // temp.Deserialize(s);
    // std::cout<<temp.x_<<std::endl;
    // std::cout<<temp.y_<<std::endl;
    // std::cout<<temp.op_<<std::endl;

    // 做到了网络功能和服务功能的解耦

    if (argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }
    MyDaemon();//调用，使进程变成守护进程
    std::unique_ptr<TcpServer> server(new TcpServer(atoi(argv[1])));
    server->BindService(calculator);
    server->Start();

    Request req(123, 456, '+');
    std::string s = req.Serialize();
    std::cout << s << std::endl;

    // Request temp;
    // temp.Deserialized(s);
    // std::cout << temp.x_ << std::endl;
    // std::cout << temp.op_ << std::endl;
    // std::cout << temp.y_ << std::endl;
    return 0;

    return 0;
}